/*
 * Galaxium Messenger
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;

using Gdk;
using Gtk;
using G=Gtk;
using Pango;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Protocol;

namespace Galaxium.Gui.GtkGui
{
	public class MessageEntryWidget: G.TextView
	{
		public event EventHandler<DragReceivedEventArgs> DragReceived;
		
		bool _showURLs = true;
		bool _showEmoticons = true;
		IConversation _conversation = null;
		
		OrderedDictionary<string, IEmoticon> _emoticons = new OrderedDictionary<string, IEmoticon> ();
		
		Dictionary<G.Image, string> _imageEquivalents = new Dictionary<G.Image, string> ();
		
		bool _bold;
		bool _italic;
		bool _underline;
		bool _strikethrough;
		
		TextTag _tagBase = new TextTag ("base");
		TextTag _tagBold = new TextTag ("bold");
		TextTag _tagItalic = new TextTag ("italic");
		TextTag _tagUnderline = new TextTag ("underline");
		TextTag _tagStrikethrough = new TextTag ("strikethrough");
		TextTag _tagUri = new G.TextTag ("uri");
		
		public string Family
		{
			get { return _tagBase.Family; }
			set
			{
				_tagBase.Family = value;
				ApplyTags ();
			}
		}
		
		public double Size
		{
			get { return _tagBase.SizePoints; }
			set
			{
				if ((value < 1) || (value > 36))
					return;
				
				_tagBase.SizePoints = value;
				ApplyTags ();
			}
		}
		
		public bool Bold
		{
			get { return _bold; }
			set
			{
				_bold = value;
				ApplyTags ();
			}
		}
		
		public bool Italic
		{
			get { return _italic; }
			set
			{
				_italic = value;
				ApplyTags ();
			}
		}
		
		public bool Underline
		{
			get { return _underline; }
			set
			{
				_underline = value;
				ApplyTags ();
			}
		}
		
		public bool Strikethrough
		{
			get { return _strikethrough; }
			set
			{
				_strikethrough = value;
				ApplyTags ();
			}
		}
		
		public bool ShowURLs
		{
			get { return _showURLs; }
			set
			{
				_showURLs = value;
			}
		}
		
		public bool ShowEmoticons
		{
			get { return _showEmoticons; }
			set
			{
				_showEmoticons = value;
				RebuildEmoticonList ();
			}
		}
		
		public IConversation Conversation
		{
			get { return _conversation; }
			set
			{
				_conversation = value;
				RebuildEmoticonList ();
			}
		}
		
		public string Text
		{
			get
			{
				string text = string.Empty;
				TextIter iter = Buffer.StartIter;
				
				do
				{
					if (iter.ChildAnchor != null)
					{
						text += _imageEquivalents[(G.Image)iter.ChildAnchor.Widgets[0]];
						continue;
					}
					
					text += iter.Char;
				}
				while (iter.ForwardChar());
				
				return text;
			}
		}
		
		public Gdk.Color Color
		{
			get { return _tagBase.ForegroundGdk; }
			set
			{
				_tagBase.ForegroundGdk = value;
				ApplyTags ();
			}
		}
		
		public int ColorInt
		{
			get
			{
				byte r = (byte)(Color.Red >> 8);
				byte g = (byte)(Color.Green >> 8);
				byte b = (byte)(Color.Blue >> 8);
				
				return (r << 16) + (g << 8) + b;
			}
			set
			{
				byte r = (byte)((value & 0xFF0000) >> 16);
				byte g = (byte)((value & 0xFF00) >> 8);
				byte b = (byte)(value & 0xFF);
				
				Color = new Gdk.Color (r, g, b);
			}
		}
		
		public MessageEntryWidget (IConversation conversation)
		{
			// Targets
			TargetEntry entryTarget = new TargetEntry ();
			entryTarget.Flags = TargetFlags.Widget;
			entryTarget.Target = "text/uri-list";
			
			G.Drag.DestSetTargetList (this, new TargetList (new TargetEntry[] {entryTarget}));
			
			AcceptsTab = true;
			BorderWidth = 0;
			CursorVisible = true;
			Editable = true;
			WrapMode = G.WrapMode.Word;
			
			Buffer.TagTable.Add (_tagBase);
			
			_tagBold.Weight = Weight.Bold;
			Buffer.TagTable.Add (_tagBold);

			_tagItalic.Style = Pango.Style.Italic;
			Buffer.TagTable.Add (_tagItalic);
			
			_tagUnderline.Underline = Pango.Underline.Single;
			Buffer.TagTable.Add (_tagUnderline);
			
			_tagStrikethrough.Strikethrough = true;
			Buffer.TagTable.Add (_tagStrikethrough);
			
			//TODO: can this be gotten from the gtk theme?
			_tagUri.ForegroundGdk = new Gdk.Color (0, 0, 255);
			_tagUri.Underline = Pango.Underline.Single;
			Buffer.TagTable.Add (_tagUri);
			
			ResetFont ();
			
			Conversation = conversation;
		}
		
		public void Clear ()
		{
			Buffer.Clear ();
			_imageEquivalents.Clear ();
		}
		
		protected override void OnDragDataReceived (Gdk.DragContext context, int x, int y, SelectionData selection, uint info, uint time)
		{
			if (selection != null && selection.Text != null)
			{
				string uristring = selection.Text;
				List<string> uris = new List<string> ();
				
				while (uristring.IndexOf("\r\n") >= 0)
				{
					uris.Add(uristring.Substring(0, uristring.IndexOf("\r\n")));
					uristring = uristring.Substring (uristring.IndexOf("\r\n")+2);
				}
				
				if (DragReceived != null)
					DragReceived (this, new DragReceivedEventArgs (uris.ToArray()));
				
				G.Drag.Finish(context, true, false, time);
			}
		}
		
		TextIter GetCurrentWordStartIter ()
		{
			TextIter iter = Buffer.GetIterAtOffset (Buffer.CursorPosition);
			
			while (!iter.IsStart)
			{
				iter.BackwardChar ();
				
				if ((!string.IsNullOrEmpty (iter.Char)) && char.IsWhiteSpace (iter.Char, 0))
					break;
			}
			if (!iter.IsStart)
				iter.ForwardChar ();
			
			return iter;
		}
		
		TextIter GetCurrentWordEndIter ()
		{
			TextIter iter = Buffer.GetIterAtOffset (Buffer.CursorPosition);
			while (!iter.IsEnd)
			{
				if ((!string.IsNullOrEmpty (iter.Char)) && char.IsWhiteSpace (iter.Char, 0))
					break;
				
				iter.ForwardChar ();
			}
			
			return iter;
		}
		
		bool IsUrl (string str)
		{
			foreach (string urlStart in MessageUtility.UriStarts)
				if (str.StartsWith (urlStart, StringComparison.InvariantCultureIgnoreCase))
					return true;
			
			return false;
		}
		
		[GLib.ConnectBefore]
		protected override bool OnKeyPressEvent (EventKey evnt)
		{
			bool ret = base.OnKeyPressEvent (evnt);
			
			TextIter iterWordStart = GetCurrentWordStartIter ();
			TextIter iterWordEnd = GetCurrentWordEndIter ();
			
			string word = iterWordStart.GetText (iterWordEnd);
			
			if (!string.IsNullOrEmpty (word))
			{
				//Log.Debug ("Current word = '{0}'", word);
				
				if (_emoticons.ContainsKey (word))
				{
					// This is an emoticon, try to load the image
					G.Image img = TextMessageDisplay.LoadEmoticon (_emoticons[word]);
					
					if (img != null)
					{
						// Delete the emoticons text
						Buffer.Delete (ref iterWordStart, ref iterWordEnd);
						
						// Add the emoticons image
						TextChildAnchor anchor = Buffer.CreateChildAnchor (ref iterWordEnd);
						AddChildAtAnchor (img, anchor);
						_imageEquivalents.Add (img, word);
					}
				}
				else if (IsUrl (word))
					Buffer.ApplyTag (_tagUri, iterWordStart, iterWordEnd);
				else
					Buffer.RemoveTag (_tagUri, iterWordStart, iterWordEnd);
			}
			
			ApplyTags ();
			
			return ret;
		}
		
		public void InsertEmoticon (IEmoticon emoticon, int index)
		{
			string txt = Buffer.GetSlice(Buffer.StartIter, Buffer.EndIter, true);
			
			G.Image img = TextMessageDisplay.LoadEmoticon (emoticon);
			
			if (img == null)
				return;
			
			TextIter iter = Buffer.GetIterAtOffset(index);
			
			G.TextChildAnchor anchor = Buffer.CreateChildAnchor (ref iter);
			
			AddChildAtAnchor (img, anchor);
			
			_imageEquivalents.Add (img, emoticon.Equivalents[0]);
		}
		
		void RebuildEmoticonList ()
		{
			_emoticons = null;
			
			if ((!_showEmoticons) || (_conversation == null))
				return;
			
			_emoticons = EmoticonUtility.GetEmoticonDict (_conversation.Session.Account, null, null, true);
		}
		
		void ApplyTags ()
		{
			Buffer.RemoveTag (_tagBase, Buffer.StartIter, Buffer.EndIter);
			Buffer.RemoveTag (_tagBold, Buffer.StartIter, Buffer.EndIter);
			Buffer.RemoveTag (_tagItalic, Buffer.StartIter, Buffer.EndIter);
			Buffer.RemoveTag (_tagStrikethrough, Buffer.StartIter, Buffer.EndIter);
			Buffer.RemoveTag (_tagUnderline, Buffer.StartIter, Buffer.EndIter);
			
			Buffer.ApplyTag (_tagBase, Buffer.StartIter, Buffer.EndIter);
			
			if (_bold)
				Buffer.ApplyTag (_tagBold, Buffer.StartIter, Buffer.EndIter);
			if (_italic)
				Buffer.ApplyTag (_tagItalic, Buffer.StartIter, Buffer.EndIter);
			if (_underline)
				Buffer.ApplyTag (_tagUnderline, Buffer.StartIter, Buffer.EndIter);
			if (_strikethrough)
				Buffer.ApplyTag (_tagStrikethrough, Buffer.StartIter, Buffer.EndIter);
		}
		
		public void ResetFont ()
		{
			Bold = false;
			Italic = false;
			Underline = false;
			Strikethrough = false;
			Family = string.Empty;
			Size = 10;
			ColorInt = 0;
			
			ApplyTags ();
		}
	}
}
